#include "act_set.hpp"
#include "../action_constants.hpp"
#include "../transaction.hpp"

using etcdserverpb::Compare;
using etcdserverpb::PutRequest;

namespace etcdv3 {

AsyncSetAction::AsyncSetAction(const ActionParameters& param, bool create)
    : Action(param) {
    Transaction transaction(parameters.key);
    isCreate = create;
    transaction.init_compare(Compare::CompareResult::Compare_CompareResult_EQUAL,
                             Compare::CompareTarget::Compare_CompareTarget_VERSION);

    transaction.setup_basic_create_sequence(parameters.key, parameters.value, parameters.lease_id);
    if (isCreate) {
        transaction.setup_basic_failure_operation(parameters.key);
    } else {
        transaction.setup_set_failure_operation(parameters.key, parameters.value, parameters.lease_id);
    }

    context.set_deadline(parameters.gainDeadline());
    reader.reset(parameters.kv_stub->AsyncTxn(&context, transaction.txn_request, &cq_).release());

    reader->Finish(&reply, &status, (void*)this);
}

AsyncTxnResponse AsyncSetAction::ParseResponse() {
    AsyncTxnResponse txn_resp;
    if (!status.ok()) {
        txn_resp.set_error_code(status.error_code());
        txn_resp.set_error_message(status.error_message());
    } else {
        txn_resp.ParseResponse(parameters.key, parameters.withPrefix, reply);
        txn_resp.set_action(isCreate ? CREATE_ACTION : SET_ACTION);

        if (!reply.succeeded() && isCreate == true) {
            txn_resp.set_error_code(105);
            txn_resp.set_error_message("Key already exists");
        }
    }
    return txn_resp;
}

///////////////////////////////////////////////////////////////////////////

AsyncPutAction::AsyncPutAction(const ActionParameters& param)
    : Action(param) {

    PutRequest put_req;
    put_req.set_key(parameters.key);
    put_req.set_value(parameters.value);
    put_req.set_prev_kv(true);
    put_req.set_lease(parameters.lease_id);

    context.set_deadline(parameters.gainDeadline());
    reader.reset(parameters.kv_stub->AsyncPut(&context, put_req, &cq_).release());

    reader->Finish(&reply, &status, (void*)this);
}

AsyncPutResponse AsyncPutAction::ParseResponse() {
    AsyncPutResponse put_resp;
    if (!status.ok()) {
        put_resp.set_error_code(status.error_code());
        put_resp.set_error_message(status.error_message());
    } else {
        put_resp.ParseResponse(reply, parameters.withPrefix);
        put_resp.set_action(PUT_ACTION);
    }
    return put_resp;
}

}
